/*-
 *  Copyright (C) 2009 Peter Baldwin   
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.peterbaldwin.client.android.wildlife;

import java.io.File;
import java.io.FileNotFoundException;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;

/**
 * Provides read/write access to any file in {@link Context#getCacheDir()} or
 * {@link Context#getFilesDir()}. Use permissions if these directories contain
 * sensitive data.
 */
public class FileProvider extends ContentProvider {
	public static final String AUTHORITY = "org.peterbaldwin.wildlife.files";

	private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);

	public static final Uri CACHE_CONTENT_URI = Uri.withAppendedPath(
			CONTENT_URI, "cache");
	public static final Uri FILES_CONTENT_URI = Uri.withAppendedPath(
			CONTENT_URI, "files");

	private static final int CACHE = 1;
	private static final int FILES = 2;

	private static UriMatcher sUriMatcher;

	{
		sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		sUriMatcher.addURI(AUTHORITY, "cache", CACHE);
		sUriMatcher.addURI(AUTHORITY, "files", FILES);
	}

	public static final String PARAM_FILE_NAME = "fileName";
	public static final String PARAM_CONTENT_TYPE = "contentType";

	public static String getContentType(Uri uri) {
		return uri.getQueryParameter(PARAM_CONTENT_TYPE);
	}

	@Override
	public boolean onCreate() {
		return true;
	}

	@Override
	public String getType(Uri uri) {
		// Depend on the caller to know the content type
		// and encode it in the URI.
		return uri.getQueryParameter(PARAM_CONTENT_TYPE);
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		return null;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		return 0;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		try {
			File file = getFile(uri);
			MatrixCursor cursor = new MatrixCursor(projection);
			MatrixCursor.RowBuilder row = cursor.newRow();
			for (int i = 0; i < projection.length; i++) {
				String columnName = projection[i];
				if (OpenableColumns.DISPLAY_NAME.equals(columnName)) {
					String fileName = file.getName();
					row.add(fileName);
				} else if (OpenableColumns.SIZE.equals(columnName)) {
					Long length = file.length();
					row.add(length);
				} else if ("_data".equals(columnName)) {
					String path = file.getAbsolutePath();
					row.add(path);
				} else {
					row.add(null);
				}
			}
			return cursor;
		} catch (FileNotFoundException e) {
			return null;
		}
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		return 0;
	}

	private File getFile(Uri uri) throws FileNotFoundException {
		Context context = getContext();

		String fileName = uri.getQueryParameter(PARAM_FILE_NAME);

		File file;
		switch (sUriMatcher.match(uri)) {
		case CACHE:
			file = context.getCacheDir();
			break;
		case FILES:
			file = context.getFilesDir();
			break;
		default:
			throw new FileNotFoundException("no files for uri: " + uri);
		}
		file = new File(file, fileName);
		return file;
	}

	// TODO: Use ContentResolver#modeToMode(Uri,String) when exposed
	static public int modeToMode(Uri uri, String mode)
			throws FileNotFoundException {
		int modeBits;
		if ("r".equals(mode)) {
			modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
		} else if ("w".equals(mode) || "wt".equals(mode)) {
			modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
					| ParcelFileDescriptor.MODE_CREATE
					| ParcelFileDescriptor.MODE_TRUNCATE;
		} else if ("rw".equals(mode)) {
			modeBits = ParcelFileDescriptor.MODE_READ_WRITE
					| ParcelFileDescriptor.MODE_CREATE;
		} else if ("rwt".equals(mode)) {
			modeBits = ParcelFileDescriptor.MODE_READ_WRITE
					| ParcelFileDescriptor.MODE_CREATE
					| ParcelFileDescriptor.MODE_TRUNCATE;
		} else {
			throw new FileNotFoundException("Bad mode for " + uri + ": " + mode);
		}
		return modeBits;
	}

	@Override
	public ParcelFileDescriptor openFile(Uri uri, String mode)
			throws FileNotFoundException {
		File file = getFile(uri);
		int modeBits = modeToMode(uri, mode);
		if (0 != (modeBits & ParcelFileDescriptor.MODE_CREATE)) {
			File parent = file.getParentFile();
			if (!parent.exists() && !parent.mkdirs()) {
				String msg = "Unable to create parent directories";
				throw new FileNotFoundException(msg);
			}
		}
		return ParcelFileDescriptor.open(file, modeBits);
	}
}
